iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0
JavaScript

TypeScript 初學者也能看的學習指南系列 第 25

TypeScript 初學者也能看的學習指南 25 - Generics 泛型 X 參數預設值 X type 應用

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20241005/20149362x2Z5xw0m1c.png

泛型系列文章

🔗 泛型函式
🔗 泛型約束

閱讀順序:泛型函式 -> 泛型約束 -> 本篇 Ꮚ・ꈊ・Ꮚ

大綱

  • 泛型參數預設值(Generic Parameter Defaults)
  • 泛型與 type alias 的應用
  • 牛刀小試

今天這篇內容比較輕鬆一點,原本想多寫泛型的進階功能 - 變異註釋,但看完後發現自己理解的不是很透徹,怕誤導大家,所以就先放棄了😊

泛型參數預設值

我們可以為泛型中的型別參數設定「預設值」。當使用泛型時,沒有提供特定的型別參數,就會自動使用預設值

語法

泛型參數預設值只需要在型別參數後使用等號(=)給定預設型別即可

範例

interface Container<T, U> {
  element: T;
  children: U;
}

// 無參數調用的情況
declare function create(): Container<HTMLDivElement, HTMLDivElement[]>; 

// 指定型別參數 T
declare function create<T extends HTMLElement>(element: T): Container<T, T[]>;

// 指定型別參數 T, U
declare function create<T extends HTMLElement, U extends HTMLElement>(
  element: T,
  children: U[]
): Container<T, U[]>;

透過為泛型參數提供預設值,我們可以將上面多個 function overloads 簡化為一個更通用的寫法:

declare function create<T extends HTMLElement = HTMLDivElement, U extends HTMLElement[] = T[]>(
  element?: T,
  children?: U
): Container<T, U>;

如果沒有提供任何參數,則T 預設為 HTMLDivElementU 預設為 HTMLDivElement[]
使用泛型參數預設值的方式大大提高了函式的靈活性和可用性,減少了overloads 的數量

使用預設值需要注意的地方

  1. 型別參數的可選性
    如果一個型別參數有預設值,同時也是代表這個參數是可選的

  2. 型別參數的順序
    require 的型別參數的位置不能在 optional 的型別參數之後

  3. 型別參數的約束滿足
    如果有為型別參數定義約束條件的話,那預設值也需要符合這些條件

  4. 型別推斷(Type Inference)與預設值
    如果指定了預設型別且型別推斷無法確定候選型別時,將被自動推斷為為預設型別
    當型別參數可以透過上下文推斷得出時,自動推斷出的型別會優先於型別參數的預設值

  5. Class 或 interface 擴展、合併
    當 Class 或 interface 在現有結構上進行擴展、合併時,可以為現有的型別參數設定預設值,新的型別參數也是可以給


泛型與 type alias 的應用

泛型也很常用在型別別名(type alias)上,如以下範例:
想了解型別別名的的話,可參考D21 - Type Alias 型別別名

// 定義一個型別別名泛型,包含 Data 和 Error
type ApiResponse<Data, Error> = {
    success: boolean;
    data: Data;
    error: Error;
};

// 使用型別別名和泛型創建具體的型別
let response1: ApiResponse<string, string> = {
    success: true,
    data: "Data loaded successfully",
    error: ""
};

let response2: ApiResponse<{ items: number }, Error> = {
    success: false,
    data: { items: 0 },
    error: new Error("Failed to load data")
};

在這個例子中
ApiResponse<Data, Error> 使得 ApiResponse 可以用來表示包含任何型別的資料和錯誤的處理
當使用型別別名泛型時,TypeScript 不會自動去做型別推斷。所以需要在使用時,明確指定型別參數,不然也可以像上方說的指定預設值

牛刀小試

嘗試將 UserProduct 重構,合併成一個自訂的 type,並使用泛型來進行優化

type User = {
    id: string;
    data: {
        name: string;
        age: number;
    };
}

type Product = {
    id: string;
    data: {
        name: string;
        price: number;
    };
}


function createUser(id: string, data: { name: string; age: number; }): User {
    return { id, data };
}

function createProduct(id: string, data: { name: string; price: number; }): Product {
    return { id, data };
}


const userEntry = createUser("user1", { name: "John Doe", age: 30 });
const productEntry = createProduct("product1", { name: "Apple iPhone 13", price: 799 });

每天的內容有推到 github 上喔

References


上一篇
TypeScript 初學者也能看的學習指南 24 - Generics 泛型 X 泛型約束
下一篇
TypeScript 初學者也能看的學習指南 26 - 實用套件篇
系列文
TypeScript 初學者也能看的學習指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言